package GC.Test01;

/**
 * @Author kirito
 * @Date 2023/11/13 11:47
 * @PackageName:GC.Test01
 * @ClassName: CanReliveObj
 * @Description:
 * 对象的finalization机制
 *
 * Java语言提供了对象终止（finalization）机制来允许开发人员提供对象被销毁之前的自定义处理逻辑。
 *
 * 当垃圾回收器发现没有引用指向一个对象，即：垃圾回收此对象之前，总会先调用这个对象的finalize()方法。
 *
 * finalize() 方法允许在子类中被重写，用于在对象被回收时进行资源释放。通常在这个方法中进行一些资源释放和清理的工作，比如关闭文件、套接字和数据库连接等。
 *
 * 永远不要主动调用某个对象的finalize()方法I应该交给垃圾回收机制调用。理由包括下面三点：
 *
 * ● 在finalize()时可能会导致对象复活。
 * ● finalize()方法的执行时间是没有保障的，它完全由GC线程决定，极端情况下，若不发生GC，则finalize()方法将没有执行机会。
 * ● 一个糟糕的finalize()会严重影响Gc的性能。
 *
 * 从功能上来说，finalize()方法与C中的析构函数比较相似，但是Java采用的是基于垃圾回收器的自动内存管理机制，所以finalize()方法在本质上不同于C中的析构函数。
 *
 * 由于finalize()方法的存在，虚拟机中的对象一般处于三种可能的状态。
 *
 * 生存还是死亡？
 *
 * 如果从所有的根节点都无法访问到某个对象，说明对象己经不再使用了。一般来说，此对象需要被回收。但事实上，也并非是“非死不可”的，这时候它们暂时处于“缓刑”阶段。
 * 一个无法触及的对象有可能在某一个条件下“复活”自己，如果这样，那么对它的回收就是不合理的，为此，定义虚拟机中的对象可能的三种状态。如下：
 *
 * ● 可触及的：从根节点开始，可以到达这个对象。
 * ● 可复活的：对象的所有引用都被释放，但是对象有可能在finalize()中复活。
 * ● 不可触及的：对象的finalize()被调用，并且没有复活，那么就会进入不可触及状态。不可触及的对象不可能被复活，因为finalize()只会被调用一次。
 *
 * 以上3种状态中，是由于finalize()方法的存在，进行的区分。只有在对象不可触及时才可以被回收。
 *
 * 具体过程
 *
 * 判定一个对象objA是否可回收，至少要经历两次标记过程：
 *
 * 1. 如果对象objA到GC Roots没有引用链，则进行第一次标记。
 * 2. 进行筛选，判断此对象是否有必要执行finalize()方法
 * 3. 如果对象objA没有重写finalize()方法，或者finalize()方法已经被虚拟机调用过，则虚拟机视为“没有必要执行”，objA被判定为不可触及的。
 * 4. 如果对象objA重写了finalize()方法，且还未执行过，那么objA会被插入到F-Queue队列中，由一个虚拟机自动创建的、低优先级的Finalizer线程触发其finalize()方法执行。
 * 5. finalize()方法是对象逃脱死亡的最后机会，稍后GC会对F-Queue队列中的对象进行第二次标记。如果objA在finalize()方法中与引用链上的任何一个对象建立了联系，
 *  那么在第二次标记时，objA会被移出“即将回收”集合。之后，对象会再次出现没有引用存在的情况。在这个情况下，finalize方法不会被再次调用，对象会直接变成不可触及的状态，也就是说，一个对象的finalize方法只会被调用一次。
 * @Version 1.0
 */
public class CanReliveObj {
    // 类变量，属于GC Roots的一部分
    public static CanReliveObj canReliveObj;

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("调用当前类重写的finalize()方法");
        canReliveObj = this;
    }

    public static void main(String[] args) throws InterruptedException {
        canReliveObj = new CanReliveObj();
        canReliveObj = null;
        System.gc();
        System.out.println("-----------------第一次gc操作------------");
        // 因为Finalizer线程的优先级比较低，暂停2秒，以等待它
        Thread.sleep(2000);
        if (canReliveObj == null) {
            System.out.println("obj is dead");
        } else {
            System.out.println("obj is still alive");
        }

        System.out.println("-----------------第二次gc操作------------");
        canReliveObj = null;
        System.gc();
        // 下面代码和上面代码是一样的，但是 canReliveObj却自救失败了
        Thread.sleep(2000);
        if (canReliveObj == null) {
            System.out.println("obj is dead");
        } else {
            System.out.println("obj is still alive");
        }
        /*
        在第一次GC时，执行了finalize方法，但finalize()方法只会被调用一次，所以第二次该对象被GC标记并清除了。
        *   调用当前类重写的finalize()方法
            -----------------第一次gc操作------------
            obj is still alive
            -----------------第二次gc操作------------
            obj is dead
*/

    }
}